//package com.modbus.tool.job;
//
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.fazecast.jSerialComm.SerialPort;
//import com.modbus.tool.common.ProtocolType;
//import com.modbus.tool.entity.po.CollectedData;
//import com.modbus.tool.entity.po.DataPoint;
//import com.modbus.tool.entity.po.Device;
//import com.modbus.tool.mapper.CollectedDataMapper;
//import com.modbus.tool.mapper.DataPointMapper;
//import com.modbus.tool.mapper.DeviceMapper;
//import com.modbus.tool.util.ModbusRTURequestBuilder;
//import com.serotonin.modbus4j.ModbusFactory;
//import com.serotonin.modbus4j.ModbusMaster;
//import com.serotonin.modbus4j.code.DataType;
//import com.serotonin.modbus4j.exception.ErrorResponseException;
//import com.serotonin.modbus4j.exception.ModbusInitException;
//import com.serotonin.modbus4j.exception.ModbusTransportException;
//import com.serotonin.modbus4j.ip.IpParameters;
//import com.serotonin.modbus4j.locator.BaseLocator;
//import lombok.RequiredArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.scheduling.annotation.Scheduled;
//import org.springframework.stereotype.Component;
//
//import java.util.List;
//
///**
// * @Author admin
// * @Description TODO
// * @Date 2025/08/07/21:25
// */
//@Component
//@RequiredArgsConstructor
//@Slf4j
//public class CollectedDataJob {
//
//    private final DeviceMapper deviceMapper;
//
//    private final DataPointMapper dataPointMapper;
//
//    private final CollectedDataMapper collectedDataMapper;
//
//
//    // 每秒采集
//    @Scheduled(cron = "*/1 * * * * ?")
//    // 每分钟采集一次
//    // @Scheduled(cron = "0 */1 * * * ?")
//    public void execute() {
//        log.info("开始执行数据采集任务...");
//        // 查询tcp设备
//        List<Device> devices = deviceMapper.selectList(
//                new LambdaQueryWrapper<Device>()
//                        .eq(Device::getProtocolType, ProtocolType.TCP)
//                        .eq(Device::getIsActive, true)
//        );
//        if (!devices.isEmpty()) {
//            for (Device device : devices) {
//                // 查询该设备下的所有点位
//                List<DataPoint> dataPoints = dataPointMapper.selectList(
//                        new LambdaQueryWrapper<DataPoint>()
//                                .eq(DataPoint::getDeviceId, device.getId())
//                );
//                if (!dataPoints.isEmpty()) {
//                    dataPoints.forEach(dataPoint -> {
//                        Float value = tcpRead(device.getIpAddress(), device.getPort(), device.getSlaveAddress(),
//                                dataPoint.getAddressOffset().intValue());
//                        log.info("设备ID: {}, 数据点ID: {}, 采集值: {}", device.getId(), dataPoint.getId(), value);
//                        if (value != null) {
//                            CollectedData collectedData = new CollectedData();
//                            collectedData.setDeviceId(device.getId());
//                            collectedData.setValue(value);
//                            collectedData.setPointId(dataPoint.getId());
//                            collectedDataMapper.insert(collectedData);
//                            log.info("数据采集成功，设备ID: {}, 数据点ID: {}, 采集值: {}", device.getId(), dataPoint.getId(), value);
//                        }
//                    });
//                }
//
//            }
//        }
//        // 查询rtu设备
//        List<Device> rtuDevices = deviceMapper.selectList(
//                new LambdaQueryWrapper<Device>()
//                        .eq(Device::getProtocolType, ProtocolType.RTU)
//                        .eq(Device::getIsActive, true)
//        );
//        if (!rtuDevices.isEmpty()) {
//            for (Device device : rtuDevices) {
//                // 查询该设备下的所有点位
//                List<DataPoint> dataPoints = dataPointMapper.selectList(
//                        new LambdaQueryWrapper<DataPoint>()
//                                .eq(DataPoint::getDeviceId, device.getId())
//                );
//                if (!dataPoints.isEmpty()) {
//                    dataPoints.forEach(dataPoint -> {
//                        Float value = rtuRead(device.getSerialPort(), device.getBaudRate(), device.getParity(),
//                                device.getDataBits(), device.getStopBits(), device.getSlaveAddress(),
//                                dataPoint.getAddressOffset().intValue());
//                        log.info("设备ID: {}, 数据点ID: {}, 采集值: {}", device.getId(), dataPoint.getId(), value);
//                        if (value != null) {
//                            CollectedData collectedData = new CollectedData();
//                            collectedData.setDeviceId(device.getId());
//                            collectedData.setValue(value);
//                            collectedData.setPointId(dataPoint.getId());
//                            collectedDataMapper.insert(collectedData);
//                            log.info("数据采集成功，设备ID: {}, 数据点ID: {}, 采集值: {}", device.getId(), dataPoint.getId(), value);
//                        }
//                    });
//                }
//            }
//        }
//        // 查询rtu over tcp设备
//        List<Device> rtuOverTcpDevices = deviceMapper.selectList(
//                new LambdaQueryWrapper<Device>()
//                        .eq(Device::getProtocolType, ProtocolType.RTU_OVER_TCP)
//                        .eq(Device::getIsActive, true)
//        );
//        if (!rtuOverTcpDevices.isEmpty()) {
//            for (Device device : rtuOverTcpDevices) {
//                // 查询该设备下的所有点位
//                List<DataPoint> dataPoints = dataPointMapper.selectList(
//                        new LambdaQueryWrapper<DataPoint>()
//                                .eq(DataPoint::getDeviceId, device.getId())
//                );
//                if (!dataPoints.isEmpty()) {
//                    dataPoints.forEach(dataPoint -> {
//                        Float value = rtuOverTcpRead(device.getIpAddress(), device.getPort(),
//                                device.getSlaveAddress(), dataPoint.getAddressOffset().intValue());
//                        log.info("设备ID: {}, 数据点ID: {}, 采集值: {}", device.getId(), dataPoint.getId(), value);
//                        if (value != null) {
//                            CollectedData collectedData = new CollectedData();
//                            collectedData.setDeviceId(device.getId());
//                            collectedData.setValue(value);
//                            collectedData.setPointId(dataPoint.getId());
//                            collectedDataMapper.insert(collectedData);
//                            log.info("数据采集成功，设备ID: {}, 数据点ID: {}, 采集值: {}", device.getId(), dataPoint.getId(), value);
//                        }
//                    });
//                }
//            }
//        }
//    }
//
//    public Float tcpRead(String host, int port, int slaveId, int startOffset) {
//        // 1. 创建TCP连接参数
//        IpParameters ipParams = new IpParameters();
//        ipParams.setHost(host); // PLC/模拟器IP
//        ipParams.setPort(port); // 默认端口
//
//        // 2. 创建Modbus
//        ModbusFactory factory = new ModbusFactory();
//        ModbusMaster master = factory.createTcpMaster(ipParams, true); // true: 使用长连接
//
//        try {
//            // 3. 初始化连接
//            master.init();
//            System.out.println("TCP连接成功");
//
//            // 4. 读取保持寄存器 (功能码03)
//            BaseLocator<Number> locator = BaseLocator.holdingRegister(
//                    slaveId, startOffset, DataType.FOUR_BYTE_FLOAT
//            );
//            Number value = master.getValue(locator);
//            return value.floatValue();
//        } catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
//            e.printStackTrace();
//            return null;
//        } finally {
//            // 6. 关闭连接
//            master.destroy();
//        }
//    }
//
//    public Float rtuRead(String commPort, int baudRate, int parity, int numDataBits, int numStopBits, int slaveId,
//                         int startOffset) {
//        // 设置串口一些参数，除了串口号其他基本固定 COM2就是文章前配置的
//        SerialPort serialPort = SerialPort.getCommPort(commPort); // 串口设备名称
//        serialPort.setBaudRate(baudRate); // 波特率
//        serialPort.setNumDataBits(numDataBits); // 数据位
//        serialPort.setParity(parity); // 校验位
//        serialPort.setNumStopBits(numStopBits); // 停止位
//
//        if (serialPort.openPort()) {
//            // 借助工具类，构建请求报文。表示：向从站地址1，发送功能3，寄存器起始地址0，读取寄存器数量10的请求（从起始地址开始，向后读取10个数据）
//            byte[] requestMessage = ModbusRTURequestBuilder.buildModbusRTURequest(slaveId, 3, 0, 10);
//            // 读取数据
//            serialPort.writeBytes(requestMessage, requestMessage.length);
//
//            try {
//                Thread.sleep(100); // 等待足够的时间以确保传感器响应
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            // 响应数据的长度根据以下公式自行调整
//            // 报文长度组成：从站地址 + 功能码 + 数据总长度 + 数据个数 + CRC校验（2个）
//            // 返回报文示例 [1, 3, 20, 0, 0, 0, 0, 0, 2, 39, 15, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 45, 30]
//            //             从站地址(1) + 功能码(1) + 数据总长度(1) + 数据个数(20) + CRC校验(2)
//            // 数据总长度20的原因：读取10个寄存器的数据，会返回*2的字节数
//            byte[] responseData = new byte[25];
//
//            // 获取响应
//            int bytesRead = serialPort.readBytes(responseData, responseData.length);
//
//            // 替换原来的解析部分
//            if (bytesRead == responseData.length) {
//                // 解析为float类型，10个float值
//                float[] floatValues = parseModbusResponseAsFloat(responseData, 3, 5);
//                serialPort.closePort();
//                return floatValues[startOffset/2];
//            } else {
//                serialPort.closePort();
//                throw new RuntimeException("未收到完整的响应数据");
//            }
//        } else {
//            throw new RuntimeException("无法打开串口: " + commPort);
//        }
//    }
//
//    public Float rtuOverTcpRead(String host, int port, int slaveId, int startOffset) {
//        // 1. 配置IP参数（指定使用RTU编码）
//        IpParameters ipParams = new IpParameters();
//        ipParams.setHost(host); // 网关IP
//        ipParams.setPort(port);           // 网关端口
//        ipParams.setEncapsulated(true);    // 关键：启用RTU封装
//
//        // 2. 创建Modbus Master
//        ModbusFactory factory = new ModbusFactory();
//        ModbusMaster master = factory.createTcpMaster(ipParams, false); // 短连接
//
//        try {
//            // 3. 初始化连接
//            master.init();
//            // 4. 读取保持寄存器 (功能码03)
//            BaseLocator<Number> locator = BaseLocator.holdingRegister(
//                    slaveId, startOffset, DataType.FOUR_BYTE_FLOAT
//            );
//            Number value = master.getValue(locator);
//            return value.floatValue();
//        } catch (Exception e) {
//            e.printStackTrace();
//            return null;
//        } finally {
//            master.destroy();
//        }
//    }
//
//    /**
//     * 解析Modbus RTU响应数据为Float类型
//     *
//     * @param responseData   响应数据字节数组
//     * @param dataStartIndex 数据起始索引
//     * @param floatCount     float数量（每个float占用2个寄存器）
//     * @return 解析后的Float值数组
//     */
//    private static float[] parseModbusResponseAsFloat(byte[] responseData, int dataStartIndex, int floatCount) {
//        float[] floatValues = new float[floatCount];
//
//        // 每个float占用4个字节(2个寄存器)，按大端序解析
//        for (int i = 0; i < floatCount; i++) {
//            // 获取组成float的4个字节
//            int byte1 = responseData[dataStartIndex + (i * 4)] & 0xFF;     // a
//            int byte2 = responseData[dataStartIndex + (i * 4) + 1] & 0xFF; // b
//            int byte3 = responseData[dataStartIndex + (i * 4) + 2] & 0xFF; // c
//            int byte4 = responseData[dataStartIndex + (i * 4) + 3] & 0xFF; // d
//
//            // 组合成32位整数 (ABCD格式)
//            int intValue = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
//
//            // 转换为float
//            floatValues[i] = Float.intBitsToFloat(intValue);
//        }
//
//        return floatValues;
//    }
//
//}
package com.modbus.tool.job;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fazecast.jSerialComm.SerialPort;
import com.modbus.tool.common.ProtocolType;
import com.modbus.tool.entity.po.CollectedData;
import com.modbus.tool.entity.po.DataPoint;
import com.modbus.tool.entity.po.Device;
import com.modbus.tool.mapper.CollectedDataMapper;
import com.modbus.tool.mapper.DataPointMapper;
import com.modbus.tool.mapper.DeviceMapper;
import com.modbus.tool.util.ModbusRTURequestBuilder;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @Author admin
 * @Description 定时采集Modbus设备数据任务
 * @Date 2025/08/07/21:25
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class CollectedDataJob {

    private final DeviceMapper deviceMapper;

    private final DataPointMapper dataPointMapper;

    private final CollectedDataMapper collectedDataMapper;


    // 每秒采集
//    @Scheduled(cron = "*/1 * * * * ?")
    // 每分钟采集一次
    @Scheduled(cron = "0 */1 * * * ?")
    public void execute() {
        log.info("开始执行数据采集任务...");
        try {
            // 查询并处理TCP设备
            processDevicesByProtocol(ProtocolType.TCP);

            // 查询并处理RTU设备
            processDevicesByProtocol(ProtocolType.RTU);

            // 查询并处理RTU over TCP设备
            processDevicesByProtocol(ProtocolType.RTU_OVER_TCP);

            log.info("数据采集任务执行完成");
        } catch (Exception e) {
            log.error("数据采集任务执行异常", e);
        }
    }

    /**
     * 根据协议类型处理设备数据采集
     *
     * @param protocolType 协议类型
     */
    private void processDevicesByProtocol(int protocolType) {
        List<Device> devices = deviceMapper.selectList(
                new LambdaQueryWrapper<Device>()
                        .eq(Device::getProtocolType, protocolType)
                        .eq(Device::getIsActive, true)
        );

        if (devices.isEmpty()) {
            return;
        }

        for (Device device : devices) {
            try {
                processDeviceData(device);
            } catch (Exception e) {
                log.error("处理设备[id={}]数据时发生异常", device.getId(), e);
                // 更新设备错误信息
                device.setLastError(LocalDateTime.now());
                device.setErrorMessage(e.getMessage());
                deviceMapper.updateById(device);
            }
        }
    }

    /**
     * 处理单个设备的数据采集
     *
     * @param device 设备对象
     */
    private void processDeviceData(Device device) {
        // 查询该设备下的所有点位
        List<DataPoint> dataPoints = dataPointMapper.selectList(
                new LambdaQueryWrapper<DataPoint>()
                        .eq(DataPoint::getDeviceId, device.getId())
        );

        if (dataPoints.isEmpty()) {
            return;
        }

        long startTime = System.currentTimeMillis();
        for (DataPoint dataPoint : dataPoints) {
            try {
                Float value = readDeviceData(device, dataPoint);
                if (value != null) {
                    CollectedData collectedData = new CollectedData();
                    collectedData.setDeviceId(device.getId());
                    collectedData.setValue(value);
                    collectedData.setPointId(dataPoint.getId());
                    collectedData.setCollectedAt(LocalDateTime.now());
                    collectedDataMapper.insert(collectedData);
                    log.debug("数据采集成功，设备ID: {}, 数据点ID: {}, 采集值: {}", device.getId(), dataPoint.getId(), value);
                }
            } catch (Exception e) {
                log.error("采集设备[id={}]数据点[id={}]数据时发生异常", device.getId(), dataPoint.getId(), e);
            }
        }
        // 更新设备采集状态
        long duration = System.currentTimeMillis() - startTime;
        device.setLastSuccess(LocalDateTime.now());
        device.setLastDuration((int) duration);
        deviceMapper.updateById(device);
        log.info("设备ID: {} 数据采集完成，耗时: {}ms", device.getId(), duration);
    }

    /**
     * 根据设备协议类型读取数据
     *
     * @param device    设备对象
     * @param dataPoint 数据点
     * @return 读取的值
     */
    private Float readDeviceData(Device device, DataPoint dataPoint) {
        int startOffset = dataPoint.getAddressOffset() != null ? dataPoint.getAddressOffset().intValue() : 0;
        switch (device.getProtocolType()) {
            case ProtocolType.TCP:
                return tcpRead(device.getIpAddress(), device.getPort(), device.getSlaveAddress(), startOffset);
            case ProtocolType.RTU:
                return rtuRead(device.getSerialPort(), device.getBaudRate(), device.getParity(),
                        device.getDataBits(), device.getStopBits(), device.getSlaveAddress(), startOffset);
            case ProtocolType.RTU_OVER_TCP:
                return rtuOverTcpRead(device.getIpAddress(), device.getPort(),
                        device.getSlaveAddress(), startOffset);
            default:
                log.warn("不支持的协议类型: {}", device.getProtocolType());
                return null;
        }
    }

    public Float tcpRead(String host, int port, int slaveId, int startOffset) {
        // 1. 创建TCP连接参数
        IpParameters ipParams = new IpParameters();
        ipParams.setHost(host); // PLC/模拟器IP
        ipParams.setPort(port); // 默认端口

        // 2. 创建Modbus
        ModbusFactory factory = new ModbusFactory();
        ModbusMaster master = factory.createTcpMaster(ipParams, true); // true: 使用长连接

        try {
            // 3. 初始化连接
            master.init();
            log.debug("TCP连接成功 {}:{}", host, port);

            // 4. 读取保持寄存器 (功能码03)
            BaseLocator<Number> locator = BaseLocator.holdingRegister(
                    slaveId, startOffset, DataType.FOUR_BYTE_FLOAT
            );
            Number value = master.getValue(locator);
            return value != null ? value.floatValue() : null;
        } catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
            log.error("TCP读取失败 {}:{}, slaveId={}, offset={}", host, port, slaveId, startOffset, e);
            return null;
        } finally {
            // 6. 关闭连接
            master.destroy();
        }
    }

    public Float rtuRead(String commPort, int baudRate, int parity, int numDataBits, int numStopBits, int slaveId,
                         int startOffset) {
        // 设置串口一些参数，除了串口号其他基本固定 COM2就是文章前配置的
        SerialPort serialPort = SerialPort.getCommPort(commPort); // 串口设备名称
        serialPort.setBaudRate(baudRate); // 波特率
        serialPort.setNumDataBits(numDataBits); // 数据位
        serialPort.setParity(parity); // 校验位
        serialPort.setNumStopBits(numStopBits); // 停止位

        if (serialPort.openPort()) {
            try {
                // 借助工具类，构建请求报文。表示：向从站地址1，发送功能3，寄存器起始地址0，读取寄存器数量10的请求（从起始地址开始，向后读取10个数据）
                byte[] requestMessage = ModbusRTURequestBuilder.buildModbusRTURequest(slaveId, 3, 0, 10);
                // 读取数据
                serialPort.writeBytes(requestMessage, requestMessage.length);

                try {
                    Thread.sleep(100); // 等待足够的时间以确保传感器响应
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("RTU读取线程被中断", e);
                }
                // 响应数据的长度根据以下公式自行调整
                // 报文长度组成：从站地址 + 功能码 + 数据总长度 + 数据个数 + CRC校验（2个）
                // 返回报文示例 [1, 3, 20, 0, 0, 0, 0, 0, 2, 39, 15, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 45, 30]
                //             从站地址(1) + 功能码(1) + 数据总长度(1) + 数据个数(20) + CRC校验(2)
                // 数据总长度20的原因：读取10个寄存器的数据，会返回*2的字节数
                byte[] responseData = new byte[25];

                // 获取响应
                int bytesRead = serialPort.readBytes(responseData, responseData.length);

                // 替换原来的解析部分
                if (bytesRead == responseData.length) {
                    // 解析为float类型，10个float值
                    float[] floatValues = parseModbusResponseAsFloat(responseData, 3, 5);
                    return floatValues[startOffset / 2];
                } else {
                    log.error("未收到完整的响应数据，期望长度: {}, 实际长度: {}", responseData.length, bytesRead);
                    throw new RuntimeException("未收到完整的响应数据");
                }
            } catch (Exception e) {
                log.error("RTU读取失败 port={}, slaveId={}, offset={}", commPort, slaveId, startOffset, e);
                return null;
            } finally {
                serialPort.closePort();
            }
        } else {
            log.error("无法打开串口: {}", commPort);
            return null;
        }
    }

    public Float rtuOverTcpRead(String host, int port, int slaveId, int startOffset) {
        // 1. 配置IP参数（指定使用RTU编码）
        IpParameters ipParams = new IpParameters();
        ipParams.setHost(host); // 网关IP
        ipParams.setPort(port);           // 网关端口
        ipParams.setEncapsulated(true);    // 关键：启用RTU封装

        // 2. 创建Modbus Master
        ModbusFactory factory = new ModbusFactory();
        ModbusMaster master = factory.createTcpMaster(ipParams, false); // 短连接

        try {
            // 3. 初始化连接
            master.init();
            log.debug("RTU over TCP连接成功 {}:{}", host, port);
            // 4. 读取保持寄存器 (功能码03)
            BaseLocator<Number> locator = BaseLocator.holdingRegister(
                    slaveId, startOffset, DataType.FOUR_BYTE_FLOAT
            );
            Number value = master.getValue(locator);
            return value != null ? value.floatValue() : null;
        } catch (Exception e) {
            log.error("RTU over TCP读取失败 {}:{}, slaveId={}, offset={}", host, port, slaveId, startOffset, e);
            return null;
        } finally {
            master.destroy();
        }
    }

    /**
     * 解析Modbus RTU响应数据为Float类型
     *
     * @param responseData   响应数据字节数组
     * @param dataStartIndex 数据起始索引
     * @param floatCount     float数量（每个float占用2个寄存器）
     * @return 解析后的Float值数组
     */
    private static float[] parseModbusResponseAsFloat(byte[] responseData, int dataStartIndex, int floatCount) {
        float[] floatValues = new float[floatCount];

        // 每个float占用4个字节(2个寄存器)，按大端序解析
        for (int i = 0; i < floatCount; i++) {
            // 获取组成float的4个字节
            int byte1 = responseData[dataStartIndex + (i * 4)] & 0xFF;     // a
            int byte2 = responseData[dataStartIndex + (i * 4) + 1] & 0xFF; // b
            int byte3 = responseData[dataStartIndex + (i * 4) + 2] & 0xFF; // c
            int byte4 = responseData[dataStartIndex + (i * 4) + 3] & 0xFF; // d

            // 组合成32位整数 (ABCD格式)
            int intValue = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;

            // 转换为float
            floatValues[i] = Float.intBitsToFloat(intValue);
        }

        return floatValues;
    }

}
